package cn.xtits.xtf.common.db;

import com.alibaba.druid.pool.DruidDataSource;
import cn.xtits.xtf.common.db.shard.XTDbShardStrategy;
import cn.xtits.xtf.common.exception.XTDbException;
import cn.xtits.xtf.common.exception.XTException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.annotation.Resource;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * Created by ShengHaiJiang on 16/4/5.
 */
public class DbRouter {

    public static final Logger logger = LoggerFactory.getLogger(DbRouter.class);

    public static final String DB_KEY_AREA = "db_area";
    public static final String DB_KEY_USER = "db_user";

    public static final boolean DEFAULT_TRANSACTION_IS_AUTO_COMMIT = false;

    /**
     * 主库
     */
    private DataSource masterDatasource;

    /**
     * 地区库
     */
    private Map<String,DataSource> areaDatasourceMapping = new ConcurrentHashMap<String, DataSource>();

    /**
     * 用户库
     */
    private Map<String,DataSource> userDatasourceMapping = new ConcurrentHashMap<String, DataSource>();

    @Resource
    private XTDbShardStrategy xtDbShardStrategy;

    @Resource
    private XTDbShardStrategy userXTDbShardStrategy;

    public Connection getAreaConnection(String areaCode) {
        return getConnection(DB_KEY_AREA, xtDbShardStrategy.defaultDbShard(areaCode),DEFAULT_TRANSACTION_IS_AUTO_COMMIT);
    }


    public Connection getAreaConnection(String areaCode,boolean isAutoCommit) {
        return getConnection(DB_KEY_AREA, xtDbShardStrategy.defaultDbShard(areaCode),isAutoCommit);
    }

    public Connection getUserConnection(String userId) {
        return getConnection(DB_KEY_USER, userXTDbShardStrategy.defaultDbShard(userId),DEFAULT_TRANSACTION_IS_AUTO_COMMIT);
    }

    public Connection getUserConnection(String userId,boolean isAutoCommit) {
        return getConnection(DB_KEY_USER, userXTDbShardStrategy.defaultDbShard(userId),isAutoCommit);
    }

    /**
     * 延迟加载获取连接时加载数据源
     * @param type
     * @param key
     * @param isAutoCommit
     * @return
     */
    private Connection getConnection(String type,String key,boolean isAutoCommit) {
        DataSource dataSource = null;
        if (type.equals(DB_KEY_AREA)) {
            if (!areaDatasourceMapping.containsKey(key)) throw new XTDbException("地区routeKey错误!");
            dataSource = areaDatasourceMapping.get(key);
        }

        if (type.equals(DB_KEY_USER)) {
            if (!userDatasourceMapping.containsKey(key)) throw new XTDbException("用户routeKey错误!");
            dataSource = userDatasourceMapping.get(key);
        }
        try {
            DruidDataSource druidDataSource = (DruidDataSource)dataSource;
            druidDataSource.init();
            Connection connection = druidDataSource.getConnection();
            connection.setAutoCommit(isAutoCommit);
            return connection;
        } catch (SQLException e1) {
            logger.error("dataSource.getConnection error !,e : {}",e1);
            throw new XTException("无法获取连接,系统错误!");
        }
    }

    private boolean areaDataSourceHasCreated(String areaKey) {
        return areaDatasourceMapping.containsKey(areaKey);
    }

    private boolean userDataSourceHasCreated(String userKey) {
        return areaDatasourceMapping.containsKey(userKey);
    }

    public void putNewAreaDataSourceToArea(String areaKey,DataSource dataSource) {
        if (!areaDatasourceMapping.containsKey(areaKey)) areaDatasourceMapping.put(areaKey,dataSource);
    }

    public void putNewUserDataSourceToArea(String userKey,DataSource dataSource) {
        if (!userDatasourceMapping.containsKey(userKey)) userDatasourceMapping.put(userKey,dataSource);
    }

    public Map<String, DataSource> getAreaDatasourceMapping() {
        return areaDatasourceMapping;
    }

    public void setAreaDatasourceMapping(Map<String, DataSource> areaDatasourceMapping) {
        this.areaDatasourceMapping = areaDatasourceMapping;
    }

    public Map<String, DataSource> getUserDatasourceMapping() {
        return userDatasourceMapping;
    }

    public void setUserDatasourceMapping(Map<String, DataSource> userDatasourceMapping) {
        this.userDatasourceMapping = userDatasourceMapping;
    }

    public DataSource getMasterDatasource() {
        return masterDatasource;
    }

    public void setMasterDatasource(DataSource masterDatasource) {
        this.masterDatasource = masterDatasource;
    }
}
